/**
 *
 */
package de.cbf.cam.dsa.person.name_generator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.cbf.cam.dsa.person.extension.interfaces.AbstractNamedGenerator;
import de.cbf.cam.dsa.person.extension.interfaces.INameGenerator;
import de.cbf.cam.dsa.person.generator.entities.NameData;
import de.cbf.cam.dsa.person.generator.entities.NameDataPart;
import de.cbf.cam.dsa.person.generator.exception.NameGenerationException;
import de.cbf.cam.dsa.rcp.model.entities.CultureNameData;
import de.cbf.cam.dsa.rcp.model.entities.NameFormat;
import de.cbf.cam.dsa.rcp.model.entities.NameList;
import de.cbf.cam.dsa.rcp.model.entities.NameType;
import de.cbf.util.CBFListUtil;
import de.cbf.util.number_generator.RandomNumberGenerator;

/**
 * @author Alti
 * generator is thereby depending on person
 */
public class NameGenerator extends AbstractNamedGenerator implements INameGenerator{

	private static final Log logger = LogFactory.getLog(NameGenerator.class);

	protected RandomNumberGenerator rnd;

	public NameGenerator() {
		super();
		rnd = RandomNumberGenerator.getInstance();
		setName("Name Generator");
	}

	/**
	 * @param nameData
	 * @param desiredAttributes
	 * @return
	 * @throws NameGenerationException
	 */
	@Override
	public NameData generateName(CultureNameData nameData, List<String> desiredAttributes) throws NameGenerationException{
		NameData result = new NameData();
		//search for correct nameFormat
		List<NameFormat> formats = nameData.getFormats();
		NameFormat format = findFormatByAttributes(desiredAttributes,formats);
		result = generateName(format.getFormatNameTypes(),nameData.getNamesMap());

		return result;
	}

	/** to find a format corresponding to desired attributes
	 * @param desiredAttributes
	 * @param formats
	 * @return
	 * @throws NameGenerationException
	 */
	protected NameFormat findFormatByAttributes(List<String> desiredAttributes,List<NameFormat> formats) throws NameGenerationException {
		for (NameFormat nameFormat : formats) {
			List<String> attributes = nameFormat.getAttributes();
			if(attributes.equals(desiredAttributes)){
				return nameFormat;
			}
		}
		throw new NameGenerationException("Format with attributes: "+desiredAttributes+" not found");
	}

	/**
	 * generate the name by choosing from the names stored at the according position in the namesMap
	 * @param formatNameTypes
	 * @param namesMap
	 * @param positions
	 * @return
	 */
	protected NameData generateName(List<NameType> nameTypes,Map<String, NameList> namesMap){
		//generate result data
		NameData result = new NameData();
		List<NameDataPart> nameDataParts = new ArrayList<NameDataPart>();
		//go through list, extract nameType, choose name from map
		for (NameType nameType : nameTypes) {
			//nameType can hold references to more than one namesMapKey, names there are to be combined then
			List<String> names = combineNamesLists(nameType.getNamesMapKeys(),namesMap);

			String chosenName = "";
			//check if names available
			int namesAvailable = names.size();
			if(namesAvailable > 0){
				//get random name for part
				int randomIndex = rnd.getRandomNumber(namesAvailable);
				chosenName = names.get(randomIndex);
			}else{
				logger.warn("NameList for "+nameType+" does not contain any names, check configuration of CultureNameData");
			}
			//add attributes
			List<String> nameDataPartAttributes = new ArrayList<String>();
			nameDataPartAttributes.addAll(nameType.getNameTypeAttributes());
			NameDataPart nameDataPart = new NameDataPart();
			nameDataPart.setText(chosenName);
			nameDataPart.setAttributes(nameDataPartAttributes);
			//put name data part to list
			nameDataParts.add(nameDataPart );
		}
		result.setNameParts(nameDataParts);
		//format final string
		logger.trace("Generated name: "+result);
		return result;
	}

	protected List<String> combineNamesLists(List<String> namesMapKeys,Map<String, NameList> namesMap) {
		ArrayList<String> result = new ArrayList<String>();
		for (String key : namesMapKeys) {
			NameList list = namesMap.get(key);
			List<String> names = new ArrayList<String>();
			if(list != null){
				names.addAll(list.getNames());
			}
			List<Object> objects = CBFListUtil.mergeLists(result, names, CBFListUtil.BEHAVIOUR_LIST_ADD_NO_DOUBLE);
			result.clear();
			for (Object object : objects) {
				if (object instanceof String) {
					String name = (String) object;
					result.add(name);
				}
			}
		}
		return result;
	}





}
